package cn.iocoder.yudao.module.scrm.exception;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.scrm.annotations.FieldRepeatValidator;
import cn.iocoder.yudao.module.scrm.annotations.FieldRepeatValidators;
import cn.iocoder.yudao.module.scrm.enums.common.FieldRepeatEnum;
import cn.iocoder.yudao.module.scrm.exception.FieldRepeatException;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.annotation.Resource;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static cn.iocoder.yudao.module.scrm.enums.ErrorCodeConstants.*;

/**
 * @Author LiZhiMin
 * @Date 2024/8/21 12:19
 */

public class FieldRepeatValidatorHandler implements ConstraintValidator<FieldRepeatValidators, Object>, ApplicationContextAware {

    private FieldRepeatValidators fieldRepeatValidators;  // 存储注解信息

    static ApplicationContext application;  // 静态应用上下文

    @Resource
    private  AdminUserApi adminUserApi;

    @Override
    @SuppressWarnings("all")//注解用于忽略所有编译器警告。
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        application = applicationContext;  // 设置应用上下文
    }

    @Override
    public void initialize(FieldRepeatValidators fieldRepeatValidators) {
        this.fieldRepeatValidators = fieldRepeatValidators;  // 初始化注解
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        return fieldRepeat(o, fieldRepeatValidators);  // 验证对象是否符合约束
    }

    /**
     * 验证对象中字段的唯一性。
     * <p>
     * 该方法检查对象的字段值是否在数据库表中已存在，确保字段值的唯一性。
     * 如果是更新操作，会排除当前记录的 ID 以避免自重复检查。
     *
     * @param object                需要验证的对象，通常是一个包含字段数据的 JSON 对象。
     * @param fieldRepeatValidators 包含字段重复验证规则的注解。
     * @return 如果字段值唯一，则返回 true；否则，抛出 {@link FieldRepeatException} 异常。
     * @throws FieldRepeatException 如果发现字段值重复，则抛出此异常，包含详细的错误信息。
     */
    public   boolean fieldRepeat(Object object, FieldRepeatValidators fieldRepeatValidators) {
        //获取当前租户信息
        String tenantId = TenantContextHolder.getTenantId().toString();
        // 解析输入对象为 JSON 对象
        JSONObject jsonObject = JSONUtil.parseObj(object);
        // 获取表名
        String table = fieldRepeatValidators.tableName();
        // 获取 ID 字段名和属性名
        String idField = fieldRepeatValidators.idField();
        String idProperty = fieldRepeatValidators.idProperty();
        // 判断是否为更新操作
        boolean isUpdate = jsonObject.containsKey(idProperty);
        // 获取 ID 的值
        Object idValue = jsonObject.get(idProperty);

        // 存储错误信息
        HashMap<String, Object> errors = new HashMap<>();
        // 获取 JdbcTemplate 实例
        JdbcTemplate jdbcTemplate = application.getBean("jdbcTemplate", JdbcTemplate.class);

        // 遍历所有字段重复验证器
        for (FieldRepeatValidator validator : fieldRepeatValidators.fieldRepeatValidators()) {
            // 获取字段重复检查的枚举类型
            FieldRepeatEnum typeEnum = getFieldRepeatEnum(validator);
            if (typeEnum == null) {
                continue; // 如果类型不匹配，则跳过
            }
            // 获取字段和 SQL 相关信息
            HashMap<String, Object> fieldAndValueAndSql = getFieldAndValue(
                    typeEnum.getType(), validator, table, tenantId, isUpdate, jsonObject, idField, idValue
            );
            String sql = fieldAndValueAndSql.get("sql").toString();
            String property = fieldAndValueAndSql.get("property").toString();
            String message = fieldAndValueAndSql.get("message").toString();

            // 执行 SQL 查询并获取错误信息
            errors.putAll(executeSqlQuery(typeEnum.getType(), sql, jdbcTemplate, property, message,table));
        }

        // 如果存在错误，则抛出异常
        if (!errors.isEmpty()) {
            throw new FieldRepeatException(errors);
        }
        return true; // 没有错误，返回 true
    }

    /**
     * 根据字段重复验证器的类型获取对应的 FieldRepeatEnum 枚举值
     */
    private static FieldRepeatEnum getFieldRepeatEnum(FieldRepeatValidator validator) {
        // 判断并返回对应的枚举类型
        if (FieldRepeatEnum.COMMON.isType(validator.foreignKeyField()) &&
                "0".equals(validator.foreignKeyProperty()) &&
                "0".equals(validator.type())) {
            return FieldRepeatEnum.COMMON;
        } else if (FieldRepeatEnum.COMMON_KEY.isType(validator.type())) {
            return FieldRepeatEnum.COMMON_KEY;
        } else if (FieldRepeatEnum.COMMON_KEY_JSON.isType(validator.type())) {
            return FieldRepeatEnum.COMMON_KEY_JSON;
        }
        return null; // 如果不匹配，返回 null
    }


    /**
     * 根据输入的类型和字段验证器生成对应的SQL查询语句，并返回包含SQL、消息和属性的映射结果。
     *
     * @param type                 查询类型，用于确定生成的SQL查询类型。
     * @param fieldRepeatValidator 字段验证器，用于获取字段、外键字段等信息。
     * @param table                数据库表名。
     * @param isUpdate             是否为更新操作的标志。
     * @param jsonObjectData       包含字段数据的JSON对象。
     * @param idField              主键字段名。
     * @param idFieldValue         主键字段值。
     * @return 包含SQL查询语句、消息和属性的映射结果。
     * @throws FieldRepeatException 如果字段值为空且配置要求不能为空，则抛出异常。
     */
    private   HashMap<String, Object> getFieldAndValue(String type, FieldRepeatValidator fieldRepeatValidator,
                                                            String table, String tenantId,boolean isUpdate, JSONObject jsonObjectData, String idField, Object idFieldValue) {
        // 初始化结果映射
        HashMap<String, Object> resultMap = new HashMap<>();

        // 获取字段名和字段值
        String field = fieldRepeatValidator.field();
        Object fieldValue = null;

        // 外键字段及其属性
        String foreignKeyField = fieldRepeatValidator.foreignKeyField();
        String foreignKeyProperty = fieldRepeatValidator.foreignKeyProperty();
        Object foreignKeyFieldValue = null;

        // 字段为空时的处理规则
        String ifFieldNullValue = fieldRepeatValidator.ifFieldNullValue();

        // 获取属性、消息及SQL变量
        String property = fieldRepeatValidator.property();
        String message = fieldRepeatValidator.message();
        String sql = null;

        // 从传入的JSON对象中获取字段值
        Object jsonData = jsonObjectData.get(property);

        // 检查字段值是否为空，并根据配置决定是否抛出异常
        if (Objects.isNull(jsonData) && "0".equals(ifFieldNullValue)) {
            String errorMessage = "字段值不能为空";
            HashMap<String, Object> errorDetails = new HashMap<>();
            errorDetails.put("message", errorMessage);
            throw new FieldRepeatException(errorDetails);
        }
        fieldValue = jsonData;

        // 根据类型生成SQL查询
        if (FieldRepeatEnum.COMMON.isType(type)) {
            // 普通类型，构建简单的SQL查询
            sql = buildSqlQuery(type, table, tenantId, field, fieldValue, idField, idFieldValue, FieldRepeatEnum.COMMON.getType(), null, isUpdate);

        } else if (FieldRepeatEnum.COMMON_KEY.isType(type)) {
            // 外键类型，根据外键属性获取外键值，并构建SQL查询
            foreignKeyFieldValue = jsonObjectData.get(foreignKeyProperty);
            sql = buildSqlQuery(type, table, tenantId, field, fieldValue, idField, idFieldValue, foreignKeyField, foreignKeyFieldValue, isUpdate);

        } else if (FieldRepeatEnum.COMMON_KEY_JSON.isType(type)) {
            // 外键JSON类型，从JSON数组中解析外键值，并构建SQL查询
            Object listMap = jsonObjectData.get(foreignKeyProperty);
            JSONObject jsonMap = JSONUtil.parseArray(listMap.toString()).getJSONObject(0);
            foreignKeyFieldValue = jsonMap.get("id");
            sql = buildSqlQuery(type, table, tenantId, field, fieldValue, idField, idFieldValue, foreignKeyField, foreignKeyFieldValue, isUpdate);
        }

        // 将SQL查询、消息和属性放入结果映射中
        resultMap.put("sql", sql);
        resultMap.put("message", message);
        resultMap.put("property", property);

        return resultMap;
    }

    /**
     * 生成SQL查询语句根据类型和参数
     *
     * @param type                 执行类型 {@link FieldRepeatEnum}
     * @param table                表名
     * @param field                字段名
     * @param fieldValue           字段值
     * @param idField              ID字段名
     * @param idFieldValue         ID值
     * @param foreignKeyField      外键字段名字
     * @param foreignKeyFieldValue 外键字段值
     * @param isUpdate             是否为更新操作
     * @return SQL查询语句
     */
    private static String buildSqlQuery(String type, String table, String tenantId, String field, Object fieldValue, String idField, Object idFieldValue,
                                        String foreignKeyField, Object foreignKeyFieldValue, boolean isUpdate) {
        //初始化查询语句
        String sql = StrUtil.format("SELECT COUNT(1) 'count' FROM {} WHERE  deleted='0' and tenant_id={} and {} = '{}' ", table,tenantId, field, fieldValue);
        //是跟新就加上不等于当前id的条件
        if (isUpdate) {
            sql = StrUtil.format("{} AND {} <> {}", sql, idField, idFieldValue);
        }
        if ((!"0".equals(foreignKeyField) && foreignKeyFieldValue != null) &&
                FieldRepeatEnum.COMMON_KEY.isType(type) || FieldRepeatEnum.COMMON_KEY_JSON.isType(type)) {
            sql = StrUtil.format(sql + " and {} = {}", foreignKeyField, foreignKeyFieldValue);
        }
        return sql;
    }

    /**
     * 执行sql语句-并返回错误信息
     *
     * @param type    {@link FieldRepeatEnum} 校验类型
     * @param sql     sql语句
     * @param message 提示消息
     * @return 重复提示信息或者空
     */
    private   HashMap<String, Object> executeSqlQuery(String type, String sql, JdbcTemplate jdbcTemplate, String property, String message,String table) {
        HashMap<String, Object> executeErrors = new HashMap<>();
        Map<String, Object> resultMap = jdbcTemplate.queryForMap(sql);
        if (!resultMap.get("count").equals(0L)) {
            // 将重复字段异常全部汇总到异常表返回到前端
            executeErrors.put(property, message);
            executeErrors.put("message", message);

            //新增|修改客户|线索的数据时返回错误信息 xxx手机号,已经存在,负责人为 xxx
            if (FieldRepeatEnum.COMMON.isType(type)) {
                String resultSql="";
                if ("scrm_clue".equals(table)){//线索表查询归属人
                    // 将 COUNT(1) 'count' 替换为 * ，并添加 LIMIT 1 只查询一条记录
                    String userId="user_id";
                    resultSql = sql.replace("COUNT(1) 'count'", property+","+userId) + " LIMIT 1";
                    Map<String, Object> resultMapOne = jdbcTemplate.queryForMap(resultSql);
                    String userIdString = (String) resultMapOne.get(userId);

                    // 转换为 Long 类型
                    Long resultUserId = userIdString != null ? Long.valueOf(userIdString) : null;
                    String nickname = adminUserApi.getUserNotCheckDataPermission( resultUserId).getCheckedData().getNickname();
                    message= StrUtil.format(CLUE_REPETITION_EXIST_PHONE.getMsg(), resultMapOne.get(property),nickname);
                    executeErrors.put("message", message);
                } else if ("scrm_customer".equals(table)) {//客户表区分情况公海和客户

                    String userId="owner_user_id";
                    resultSql = sql.replace("COUNT(1) 'count'", property+","+userId) + " LIMIT 1";
                    Map<String, Object> resultMapOne = jdbcTemplate.queryForMap(resultSql);
                    Long userIdLong = (Long) resultMapOne.get(userId);
                    if (userIdLong != null) { // 客户
                        AdminUserRespDTO checkedData = adminUserApi.getUserNotCheckDataPermission(userIdLong).getCheckedData();
                        if (checkedData != null) {// TODO 防止人员被删除的情况
                            String nickname = checkedData.getNickname();
                            message = StrUtil.format(CLUE_REPETITION_CUSTOMER_PHONE.getMsg(), resultMapOne.get(property), nickname);
                        } else {
                            message = StrUtil.format(CLUE_PHONE_EXIST_BY_USER_ID_NOT.getMsg(), resultMapOne.get(property), userIdLong);
                        }
                    } else { // 公海
                        message = StrUtil.format(CLUE_TRANSFORM_EXIST_PUBLIC_CUSTOMER_PHONE.getMsg(), resultMapOne.get(property));
                    }
                    executeErrors.put("message", message);
                }
            }
        }
        return executeErrors;
    }


}
